﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

public class ProjectShadowRenderCommand : MonoBehaviour
{
    public Material shadowMaterial;
    public Transform receiver;
    public Transform shadowLight;
    public MeshRenderer target;
    private Camera _cachedCamera;
    private CommandBuffer _commandBuffer;
    void OnEnable()
    {
        _cachedCamera = GetComponent<Camera>();
        _commandBuffer = new CommandBuffer();
        _commandBuffer.SetViewMatrix(_cachedCamera.worldToCameraMatrix * CalculateMatrix());
        _commandBuffer.DrawRenderer(target, shadowMaterial);
        _commandBuffer.SetViewMatrix(_cachedCamera.worldToCameraMatrix);
        _cachedCamera.AddCommandBuffer(CameraEvent.AfterEverything, _commandBuffer);
    }

    void OnDisable()
    {
        _cachedCamera.RemoveCommandBuffer(CameraEvent.AfterEverything, _commandBuffer);
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    Matrix4x4 CalculateMatrix()
    {
        //plane pos:O,normal:N
        //light pos:l
        //plane equation: N*x - N*O = 0
        //ray equation: p(t) = l + t * (x - l) where x is the origin point
        //project equation: N*(l+t*(x-l)) - N*O = 0
        //and t is:(N*O - N*l)/(N*x - N*l)
        //and p(t) is: l + (N*O - N*l) * (x - l)/(N*x - N*l)
        //= ((N*x*l-N*l*l) + (N*O*x-N*l*x) - (N*O*l -N*l*l)) / (N*x - N*l)
        //= (N*x*l+N*O*x-N*l*x-N*O*l) / (N*x - N*l)
        //= (Nx*Xx+Ny*Xy+Nz*Xz)l + (N*O)X - (N*l)*X - (N*O)*l / (Nx*Xx+Ny*Xy+Nz*Xz - N*l)
        //project matrix:
        //Nx*lx+N*O-N*l  Ny*lx          Nz*lx           -N*O*lx
        //Nx*ly          Ny*ly+N*O-N*l  Nz*ly           -N*O*ly
        //Nx*lz          Ny*lz          Nz*lz+N*O-N*l   -N*O*lz
        //Nx             Ny             Nz              -N*l
        Vector3 O = receiver.transform.position;
        Vector3 N = receiver.transform.up;
        Vector3 l = shadowLight.transform.position;
        float ON = Vector3.Dot(O, N);
        float lN = Vector3.Dot(l, N);
        /*
        Matrix4x4 ret = new Matrix4x4(
            new Vector4(N.x*l.x + ON-lN, N.y*l.y, N.z*l.z, -ON*l.x),
            new Vector4(N.x*l.x, N.y*l.y + ON-lN, N.z*l.z, -ON*l.y),
            new Vector4(N.x*l.x, N.y*l.y, N.z*l.z + ON-lN, -ON*l.z),
            new Vector4(N.x, N.y, N.z, -lN)
        );
        */
        Matrix4x4 ret = new Matrix4x4(
            (new Vector4(N.x*l.x + ON-lN, N.x*l.y, N.x*l.z, N.x)) * -1,
            new Vector4(N.y*l.x, N.y*l.y + ON-lN, N.y*l.z, N.y) * -1,
            new Vector4(N.z*l.x, N.z*l.y, N.z*l.z + ON-lN, N.z) * -1,
            new Vector4(-ON*l.x, -ON*l.y, -ON*l.z, -lN) * -1
        );
        return ret;
    }
}
